home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power CD-ROM!! 8
/
Power CD-ROM 8.iso
/
prgmming
/
pmd110
/
manual.txt
< prev
next >
Wrap
Text File
|
1994-11-13
|
58KB
|
1,761 lines
**** W A R N I N G ****
The documentation for this program is typesetted exactly like the
Borland manuals. When this documentation is converted to plain ascii
it doesn't look very pretty. Especially graphics are distorted,
don't see chapter 4.
This manual is primarily intended to give you an idea of its
contents. Registered users receive a printed and bound manual.
Besides they receive the manual in postscript format on disk.
**** ****
Borland Pascal Debug Kit
─────────────────────────────────────────────────────────────────────────────
Programming Guide
Version 1.10
NederWare
Burgerstraat 22
5311 CX Gameren
The Netherlands
compuserve: 100120,3121
email: berend@beard.nest.nl
fidonet: 2:281/527.23
C O N T E N T S
───────────────────────────────────────────────────────────────────────────────
Introduction Program with less pain 1 Chapter 5 Assertions 23
Why this tool?........................ 1
Features ............................. 1
Part 2 Reference Manual
What's in this manual ................. 2
Typefaces used in this manual .......... 2
Chapter 6 Pascal Debug kit
How to contact NederWare ............ 3
Reference 27
Acknowledgements ................... 3
Sample procedure .................... 27
Archive constant...................... 27
Assert procedure ..................... 28
Part 1 User Manual
Beep procedure....................... 28
Chapter 1 Working with log files 7 BeepOff procedure.................... 28
How to create or open a log file ......... 7 BeepOn procedure .................... 28
How can you write information in the BrowserRecordSize variable............ 29
log file .............................. 8 BrowsersOffset variable ...............29
ClassesOffset variable .................29
Chapter 2 Checking your memory 9 ClassRecordSize variable ..............29
What you must do before MemCheck CMPB function ....................... 29
can work ............................ 9 CMPW function ...................... 30
Updating the standard library ........ 9 ConvMemLimit variable............... 30
Recompiling the system unit ......... 10 CorrelationRecordSize variable .........30
What MemCheck does ................ 10 CorrelationsOffset variable............. 30
How to enable memory checking ....... 11 CPos function ........................ 30
How MemCheck works ............... 11 CR constant .......................... 30
MemCheck and DPMI................. 12 CreateBAK procedure ................. 31
DataOffset variable ...................31
Chapter 3 Post Mortem Debugger 13
DateTime type ....................... 31
What the Post Mortem debugger does ... 13
DebugHeader variable ................ 31
Using the Post Mortem Debugger....... 14
DebugInfoStart variable ............... 31
The Post-Mortem-Debugger under
Delay variable........................ 32
MS-Windows ........................ 16
dfXXXX constants .................... 32
Extra features under Windows ....... 16
DirStr type........................... 32
GPF's under Windows .............. 16
Discard procedure .................... 32
Chapter 4 TDInfo 17 DisposeSLink procedure............... 33
How TDInfo models Borland's Debug DonePMD procedure.................. 33
Information .......................... 17 DoneStrings procedure ................ 33
Changes between Open Architecture DosCopy procedure................... 33
names and TDInfo names.............. 19 DosDel procedure .................... 34
How to make use of TDInfo ............ 20 DosDelay procedure .................. 34
Initializing the TDInfo unit...........20 DosMove procedure .................. 34
Getting information from a debug DosTouch procedure .................. 35
symbol file......................... 20 DosWipe procedure ................... 35
Getting line numbers.............. 21 DStream variable ..................... 36
Getting procedures ............... 21 DumpStack variable .................. 36
DumpStackProcedureType type ........ 36
i
Empty function....................... 36 LoadStrings procedure ................ 46
ExtractStr function .................... 36 LogError procedure ................... 46
ExtStr type........................... 36 LowCase function .................... 46
FancyStr function ..................... 37 LowStr function ...................... 46
FatalErrorText variable ................ 37 MaxConventionalMemoryBlock constant 46
FCreate function...................... 37 MaxMemPtrs constant................. 46
FDefaultExtension function ............ 37 MaxWord constant.................... 47
ferr variable.......................... 37 MemberRecordSize variable............ 47
FExpand function..................... 38 MembersOffset variable ............... 47
FF constant .......................... 38 MemCheckReport procedure ........... 47
FForceDir function.................... 38 memfXXXX constants ................. 47
FForceExtension function .............. 38 Min function ......................... 48
FileExist function ..................... 38 ModuleClassesOffset variable .......... 48
FileRec type.......................... 38 ModuleClassRecordSize variable ....... 48
fmXXXX constants .................... 39 ModuleRecordSize variable ............ 48
FOpen function....................... 39 ModulesOffset variable................ 48
FormatStr procedure .................. 39 NamesOffset variable ................. 49
FormFeed constant.................... 39 NameStr type ........................ 49
FTCopy function ..................... 40 NewSLink function ................... 49
GetAddrStr function .................. 40 OverloadRecordSize variable........... 49
GetDateStr function ................... 40 ParentRecordSize variable ............. 49
GetEnv function ...................... 40 ParentsOffset variable .................49
GetFileName function ................. 40 PathStr type.......................... 50
GetLogicalAddr function .............. 40 PrintError variable ....................50
GetObjMemory function............... 41 PrintErrorType type................... 50
GetStr function ....................... 41 prnXXXX constants ................... 50
GetTextFileName function ............. 41 PSLink type.......................... 51
GetTickCount function ................ 41 Registers type ........................ 51
GetTimeStr function................... 42 RepChar function..................... 51
GetUniqueFileName function .......... 42 ReplaceStr procedure.................. 51
HandleRunTimeError variable ......... 42 ReportFileName constant .............. 51
HandleRunTimeErrorProcedureType type 42 RightJustify function ..................52
HexB function........................ 42 rsGet function ........................ 52
HexStr function ...................... 42 rsGet1 function ....................... 52
InitBBError function .................. 43 rsGet2 function ....................... 52
InitIntHandler procedure .............. 43 ScanB function ....................... 53
InitObjMemory procedure ............. 43 ScanW function ...................... 53
InitPMD procedure ................... 43 ScopeClassesOffset variable ............53
IsDirectory function................... 44 ScopeClassRecordSize variable ......... 53
IsFileOpen function ................... 44 ScopeRecordSize variable .............. 53
IsValidPtr function.................... 44 ScopesOffset variable ................. 53
LeadingZero function ................. 44 scXXXX constants..................... 53
LeftJustify function ................... 45 SearchRec type ....................... 54
LF constant .......................... 45 SegmentRecordSize variable ........... 54
LineNumberRecordSize variable........ 45 SegmentsOffset variable ............... 54
LineNumbersOffset variable ........... 45 SetHandleCount procedure ............ 54
ii
SmallDebugHeaderSize constant ....... 55 Methods........................... 67
SmallEndianI procedure ............... 55 TObjMemory object ................... 67
SmallEndianL procedure .............. 55 Fields .............................67
SmallEndianW procedure.............. 55 Methods........................... 67
SourceFileRecordSize variable .......... 55 TOverload type....................... 68
SourceFilesOffset variable ............. 55 TParent type ......................... 68
Spc function ......................... 56 TResourceCollection object............. 69
Spoiled function ...................... 56 Methods........................... 69
StrB function ......................... 56 TResourceFile object ..................69
StrI function ......................... 56 Fields .............................69
Strings variable....................... 56 Methods........................... 69
StripSpc function ..................... 56 TResourceItem type................... 70
StrL function ......................... 57 TrimRight function.................... 70
StrR function......................... 57 TScope object ........................ 70
StrResBufSize variable................. 57 Fields .............................70
StrS function ......................... 57 Methods........................... 71
StrW function ........................ 57 TScopeClass type ..................... 71
stXXXX constants ..................... 57 TSegment object ...................... 71
SymbolRecordSize variable ............ 58 Fields .............................71
SymbolsOffset variable ................ 58 Methods........................... 72
TBrowser object ...................... 58 TSLink type.......................... 73
Fields ............................. 58 TSmartBufStream object ............... 73
Methods........................... 59 Fields .............................73
TClass object ......................... 59 Methods........................... 73
Fields ............................. 59 TSourceFile object .................... 73
Methods........................... 59 Fields .............................74
TCorrelation object.................... 59 Methods........................... 74
Fields ............................. 60 TSymbol object ....................... 74
Methods........................... 60 Fields .............................74
TDebugHeader type .................. 60 Methods........................... 74
TDInfoPresent function................ 62 TType object ......................... 75
TDriveStr type ....................... 62 Fields .............................76
TextPrintError procedure .............. 62 Methods........................... 76
TextRec type ......................... 63 TypeRecordSize variable............... 77
tid voidXXXX constants ............... 63 TypesOffset variable ..................77
TLineNumber object .................. 63 UpStr function ....................... 77
Fields ............................. 63 ValB function......................... 77
Methods........................... 63 valcode variable ...................... 77
TMember object ...................... 63 ValHex function ...................... 77
Fields ............................. 65 ValI function ......................... 78
Methods........................... 65 ValL function......................... 78
TModule object....................... 65 ValR function ........................ 78
Fields ............................. 65 ValW function........................ 78
Methods........................... 66 Warning procedure ................... 78
TModuleClass type ................... 66 ZeroRightJustify function ..............79
TObject object ........................ 67
iii
Appendix A Errors and omissions
in Open Architecture
Handbook 81
iv
I N T R O D U C T I O N
───────────────────────────────────────────────────────────────────────────────
Program with less pain
Why this tool?
───────────────────────────────────────────────────────────────────────────────
Once, I tracked down a bug that caused some strange and
unpredictable behaviour. After finally having located the problem,
it appeared that I had released a bigger block of memory than
what I had previously allocated. Borland Pascal, naturally, didn't
like this. Well, neither did I since, all in all, this bug took me 10
hours to find and fix.
Some time later, I encountered another bug that caused a
behaviour which I remembered but all too well. At that stage I
thought: let's put these 10 hours to some better use.
That thought was the start of MemCheck, a utility that tracks
memory allocations and deallocations. If you release too much
memory or too little, MemCheck aborts your application, writing
the address of the erroneous code to a log file.
MemCheck has since been enhanced to write the name of the file
and the line of code using TDInfo, a utility that accesses the Debug
information, if present, in an executable file.
The above mentioned tools, MemCheck, TDInfo and PMD, together
with Assertions, a unit which provides you with C like assertions,
form the Borland Pascal Debug Kit. It's my sincere hope that these
tools will prove useful to Pascal programmers all around the
world, helping them to develop bug-free programs easier and
faster.
Features
───────────────────────────────────────────────────────────────────────────────
The Borland Pascal Debug Kit provides you with many things that
make debugging easier. If you use these tools properly, you can
even be sure that some bugs will not occur in your programs! For
example, releasing more or less memory than previously allocated
immediately halts your program and the offending line is written
to a log file.
In summary, the Borland Pascal Debug Kit gives you:
Introduction 1
■ Allocation and deallocation tracking
■ A report of not deallocated memory after your program
termination
■ A full stack dump (procedure names and parameters) if a
run-time error occurs
■ C-like assertions to `fortify your subsystems'
What's in this manual
───────────────────────────────────────────────────────────────────────────────
This manual explains how to use the Borland Pascal Debug Kit. It
doesn't tell you how to write Borland Pascal programs.
The first part of this manual contains the following chapters:
■ Chapter 1, ``Using log files'', describes how to use log files to
make debugging easier, especially when your program is
running at a remote site.
■ Chapter 2, ``Memory Checker'', introduces the memory
allocation and deallocation tracker and explains how to use it.
■ Chapter 3, ``Post Mortem Debugger'', describes how to get
annotated stack dumps.
■ Chapter 4, ``TDInfo'', explains a unit which gives access to
Borland's debug information.
■ Chapter 5, ``Assertions'', introduces you to assertions and how
to use them in your programs.
The second part of this manual is contains reference material for
the source code supplied with the Borland Pascal Debug Kit.
Typefaces used in this manual
───────────────────────────────────────────────────────────────────────────────
This manual was typeset using LATE X2ε, converted to postscript
using dvips, and printed on a HP Laserjet IV. All typefaces used in
this manual are standard Postscript fonts. Their uses are as
follows:
The monospace typeface represents text as it appears on-screen or
in a program. It is also used for anything you must type (e.g. BP to
start up the Borland Pascal IDE).
The boldface typeface is used in text for command line options.
Italics is used to indicate identifiers that appear in text. They can
represent terms that you can use as they are, or that you can think
2 Borland Pascal Debug Kit Manual
up new names for (your choice, usually). They are also used to
emphasize certain words, such as new terms.
This typeface indicates a key on your keyboard. For example,
``Press Esc to exit this menu.''
Key combinations produced by holding down one or more keys
simultaneously are represented as Key1+Key2.
How to contact NederWare
───────────────────────────────────────────────────────────────────────────────
NederWare offers a variety of services to answer your questions
about the Borland Pascal Debug Kit. Of course, this is only true for
registered users. If you haven't registered yet, support shareware
(and me and my family) by registering this Debug Kit today.
We've made it very easy for you. You can register by CompuServe,
fax, email or telephone. Please consult REGISTER.FRM for more
details.
CompuServe
Subscribers to CompuServe can reach NederWare at 100120,3121.
The latest version of the Borland Pascal Debug Kit is available in
library 3 of the BPASCAL forum.
Internet
NederWare can be reached on the Internet through
NederWare@beard.nest.nl or berend@beard.nest.nl. For the latest
Debug Kit version, check-out garbo.uwasa.fi:/pc/turbopas.
Fidonet
NederWare can be reached on Fidonet at 2:281/527.23. The latest
version of the Borland Pascal Debug Kit is also available at
2:281/527, Contrast BBS, The Netherlands. Its full international
telephone number is +31 70-3234903. From Holland dial
070-3234903.
Acknowledgements
───────────────────────────────────────────────────────────────────────────────
First and foremost, many thanks to Stephen Maguire, who in his
excellent book [Maguire93] showed us all that writing bug free
(well, almost) code is possible. If you are serious about writing
commercial software, read this book! It inspired me to put together
all the pieces I had accumulated over the years in this package.
Introduction 3
Many thanks also to Andy McFarland, author of TDI, a utility that
displays the contents of the debug information the the BP compiler
appends to an executable. Andy generously provided me with the
source of his utility, which gave me a jump start in developing my
TDInfo unit. Andy can be reached at CompuServe as 71055,2743 or
by email as amcfarl@ndlc.occ.uky.edu.
Last but not least thanks to Dag Hovden
(dhovden@runner.knoware.nl) for cleaning up this manual and
improving its english. All remaining faults are mine.
4 Borland Pascal Debug Kit Manual
P A R T
───────────────────────────────────────────────────────────────────────────────
1
User Manual
5
6 Borland Pascal Debug Kit Manual
C H A P T E R
───────────────────────────────────────────────────────────────────────────────
1
Working with log files
Log files form the basis of Post Mortem Debugging. Using log files
means no more scribbling down error codes and addresses on a
piece of paper when your program crashes because it's already all
there, in the log file. This feature becomes especially useful when
you use it in tandem with PMD, the Post Mortem Debugger,
because PMD gives you a full symbolic stack dump. Try to write
that down as it scrolls past you on the screen!
Another benefit of using log files is that you know exactly what
your program was up to when it crashed running at a customer
site. No more long telephone conversations, simply ask the
customer to modem (or mail) you the log file.
How to create or open a log file
───────────────────────────────────────────────────────────────────────────────
The unit BBError makes using log files easy. All you have to is to
call InitBBError with the name of the logfile and a Boolean
parameter saying to create or to open the log file if it already exists.
The following program demonstrates this:
program Test;
uses
BBError;
begin
writeln('Installing log file writing');
InitBBError('TEST.LOG', TRUE);
writeln('Done!');
end.
A call to the procedure InitBBError does two things:
1. It creates the log file if it does not exists or if the second
parameter is FALSE. The file is opened in append mode if it
exists and the second parameter is TRUE.
Chapter 1, Working with log files 7
2. It installs an exit procedure which writes the error address and
exit code to the log file if an error occurs.
This exit procedure also dumps a stack trace to the log file with
the addresses of all the callers that lead up to the code that
caused the error.
How can you write information in the log file
───────────────────────────────────────────────────────────────────────────────
You can write your own data to the log file using the ferr text file
variable by adding BBError to the USES statement of any unit that
wants to write to the log file.
Example:
program WriteToferr;
uses
BBError;
begin
InitBBError('TEST.LOG', TRUE);
writeln(ferr, 'This goes to the log file.');
end.
A second method is to call BBError's LogError procedure which
first writes the current date and time to the log file and next any
text you passed to it. Calling LogError is the preferred method.
Example:
program WriteToLogError;
uses
BBError;
begin
InitBBError('TEST.LOG', TRUE);
LogError('This goes to the log file.');
end.
8 Borland Pascal Debug Kit Manual
C H A P T E R
───────────────────────────────────────────────────────────────────────────────
2
Checking your memory
Use the MemCheck unit, if you want to check if your allocations
match your deallocations or if you want to check if you didn't
overwrite memory before or after an allocated memory block. This
chapter tells you how to use MemCheck and what MemCheck does.
What you must do before MemCheck can work
───────────────────────────────────────────────────────────────────────────────
Before you can use MemCheck you need to replace the standard
SYSTEM unit by the SYSTEM unit supplied with the Borland
Pascal Debug Kit. There are two cases:
■ If you always work with the Borland Pascal library TURBO.TPL,
TPP.TPL or TPW.TPL, you need to replace the SYSTEM unit
contained in the library by the supplied one. Below we will
explain how to do this.
■ If you don't load the library at startup but instead link in the
units as separate files, simply copy the supplied SYSTEM.TPx to
the directory where you keep these units.
─────────────────────────────────────────────────────────────────────────────
Updating the
Replacing SYSTEM.TPx is quite easy. First remove the `old'
standard library
SYSTEM.TPU from TURBO.TPL (or TPP.TPL or TPW.TPL, please
substitute as needed). You can do this with the following
command:
tpumover turbo -system
Next install the new SYSTEM.TPU by:
tpumover turbo +system.tpu
To install the new SYSTEM.TPW in the windows library type:
tpumover tpw +system.tpw
Note: don't forget tot type the correct extension after sytem, e.g.
.tpu, .tpp or .tpw!
Chapter 2, Checking your memory 9
─────────────────────────────────────────────────────────────────────────────
Recompiling the
If you have the run-time library source, and if you want to
system unit
recompile the SYSTEM unit with our changes incorporated, you
need to patch the original SYSTEM.PAS, HEAP.ASM and
WMEM.ASM with the diff (i.e. difference) files SYSTEM.DIF,
HEAP.DIF and WMEM.DIF. Please note: these files are new
structure diff files, created with the GNU diff utility.
You can use the GNU patch utility (we use the one coming with
the DJGPP GNU C compiler port) to patch your run-time library
source. For example, execute
patch system.dif c:/bp/rtl/sys/system.pas
to update the SYSTEM.PAS file in the C:/BP/RTL/SYS directory.
What MemCheck does
───────────────────────────────────────────────────────────────────────────────
The MemCheck unit checks for the following three bugs:
■ Attempts to dispose a pointer, specifying a size other than the
value used when creating the pointer. This causes a program
halt.
■ Allocating memory and never deallocating it (i.e. memory
leakage). By calling MemCheckReport, you obtain a list of
pointers still allocated together with the addresses in your
program where these pointers were created.
■ Writing to memory that lies outside an allocated block of
memory, i.e. before or after the block. This check is equivalent to
the Range check ({$R+}) option for normal arrays. This bug also
causes a program halt.
The demo program TestMem demonstrates these three bugs.
Execute TestMem and select one of the three options presented to
you. Afterwards, see TESTMEM.LOG for choice 1 and 2, and
MEMCHECK.RPT for choice 3.
If you have appended debug information to your executable and if
you have initialized the post mortem debugger, MemCheck will
also print the source file, line number and procedure name where
the error occured. This is also true for MEMCHECK.RPT. When
debug informaton is appended, you will find in MEMCHECK.RPT
the source file and line number file where the allocation occured
and on the next indented line the source file and line number of
the caller of that routine (if any).
10 Borland Pascal Debug Kit Manual
How to enable memory checking
───────────────────────────────────────────────────────────────────────────────
You enable MemCheck by placing it in the USES clause of your
main program. No additional calls are needed. Please bear in
mind that using MemCheck will cost you an extra 32 bytes per
allocated memory block!
Errors detected by MemCheck are normally printed to the screen. A
much better alternative is to use MemCheck together with BBError,
the log file unit. When you use BBError the errors will be written
to a log file instead.
How MemCheck works
───────────────────────────────────────────────────────────────────────────────
MemCheck keeps track of every allocated pointer and the size
associated with it. For each allocated pointer, 16 bytes are needed
to store this information. The information itself is stored in a
collection which can hold a maximum of 16384 pointers. It is easy
to enlarge this but as yet, there has been no need for that. If you
should need more, please let me know and I'll enhance MemCheck.
If possible, MemCheck allocates 8 or 16 bytes more for every
memory block than what your program asked for. Because the
maximum memory block that can be allocated is 65536-8 bytes,
MemCheck always checks if it is possible to allocate 8 or 16 bytes
more.
■ If it is possible to allocate 16 bytes more, MemCheck is able to
check for memory overwrites before and after your allocated
memory block. MemCheck does this by filling the first and last 8
bytes with the value 0CCh. When you dispose this memory
block, MemCheck checks if these values are still there. If not,
memory has been overwritten and MemCheck halts your
program.
■ If it is possible to allocate only 8 bytes more, MemCheck only
checks for overwrites at the end of your memory block.
To recap: every pointer needs 16 bytes to store information about
it plus an extra 16 (or 8) bytes to check for 'out of range' errors.
Together, this accounts for the 32 (or 24) bytes extra per allocated
block. Under protected mode or in Windows, this is not a serious
problem. Under real mode, however, this could well be a serious
obstacle in using MemCheck, especially if your application uses
many, small memory blocks.
Chapter 2, Checking your memory 11
MemCheck and DPMI
───────────────────────────────────────────────────────────────────────────────
When running under protected mode, as some of you may know,
you can check for writing outside an allocated memory block by
setting HeapLimit to zero. This will enable the processor's built-in
segment checking. The problem with this approach is that you can
easily run out of selectors, especially if you use MemCheck at the
same time. As we've seen above, MemCheck allocates a 16 byte
memory block from the heap for every pointer (and uses that block
to store information about the pointer). With HeapLimit set to zero,
every allocation actually costs you two selectors!
12 Borland Pascal Debug Kit Manual
C H A P T E R
───────────────────────────────────────────────────────────────────────────────
3
Post Mortem Debugger
The Post Mortem Debugger gives you the ability to print full
symbolic stack dumps when an error condition occurs in your
program. This is a feature you will not want to miss as soon as you
have used it to develop programs.
What the Post Mortem debugger does
───────────────────────────────────────────────────────────────────────────────
Currently, the name debugger is a bit of a misnomer. The Post
Mortem Debugger does nothing more than hooking into the exit
procedure chain and dumping the stack, with all procedure names
and parameter values, to the log file if an error occurs.
If you have enabled the Post Mortem Debugger, MemCheck writes
actual source file names and line numbers instead of hexadecimal
addresses when it creates its report of not disposed memory.
An example of the log entries created when a program exits with a
fatal error is:
** Program started on 1994-02-26 at 19:07:38 **
Post Mortem Debugger (PMD) installed.
1994-02-26 19:07:38 Error 215 at 0001:0031
1994-02-26 19:07:38 TESTPMD.PAS (26) procedure
InSide(100,0,0);
1994-02-26 19:07:38 *** Full stack dump ***
TESTPMD.PAS (31) procedure
TestError((Open,'TESTPMD.BAK'),test2);
TESTPMD.PAS (44)
This is the log file created if you run the TestPMD program.
The advantage of a symbolic stack dump is great, perhaps far
greater than you would expect. You find errors much faster due to
the full stack trace created. Previously, you could only get such an
overview when running inside the debugger. For a frequently
Chapter 3, Post Mortem Debugger 13
called procedure, failing perhaps only every 100th time, it is not
really easy (or much fun) to use breakpoints to wait for the correct
moment to halt and view the stack.
The Post-Mortem debugger has one flaw which shows up
sometimes. If the stack trace consists of near calls, PMD may not
always be able to find the address. As soon as a far call occurs in
the stack frame, the stack dump is correct from that point on.
Currently we do not know how to fix this. Luckily this case occurs
seldom.
Using the Post Mortem Debugger
───────────────────────────────────────────────────────────────────────────────
To use the Post Mortem Debugger, you need to include three units
in your USES clause. And you need three calls to initialize these
three units. The order in which you call these routines is
important. The order in which they appear in the USES clause is
not important.
The three units are BBError, ObjMemory and PMD. The
initialization routines in their correct oder are InitBBError,
InitObjMemory and InitPMD.
The program listed below, also available as TESTPMD.PAS on
your distribution disk, demonstrates the Post Mortem Debugger.
{$Q+}
program TestPMD;
uses
BBError, BBUtil,
Objects, ObjMemory,
{$IFDEF Windows}
WinCrt,
{$ENDIF}
PMD;
type
types = (test1, test2, test3);
procedure TestError(var f : text; e : types);
procedure InSide(o : TObjMemory);
var
w : word;
d : word;
begin
w := 0;
14 Borland Pascal Debug Kit Manual
w := w - 1;
end;
begin
InSide(GetObjMemory(100, 0, memfAll)^);
end;
var
f : text;
begin
writeln(prnCR, 'Post Mortem Debugger tester.');
(* initialize *)
InitBBError('TESTPMD.LOG', TRUE);
InitObjMemory;
InitPMD(dfStandard);
(* and show the error *)
Assign(f, 'TESTPMD.PAS');
Reset(f);
TestError(f, test2);
end.
As you can see, there are three initialization routines that must be
called before PMD can be used: InitBBError to open or create a log
file, InitObjMemory to initialize the memory management routines
that PMD uses, and (finally) InitPMD to initialize the Post Mortem
Debugger itself. The InitPMD procedure accepts certain flags on
which the Post Mortem Debugger bases its behaviour. The default
is dfStandard, you will get only a symbolic stack dump when an
error occurs. If you specify the dfDataSeg flag as well
(InitPMD(dfStandard + dfDataSeg) ) the datasegment is also dumped
to the log file.
Compile the example program with full debug information
enabled by typing
tpc -m -l -v testpmd
at the Dos command line prompt. The program contains a
delibarate error, namely an overflow bug (therefore it has to be
compiled with {$Q+}).. Run it, then look at the created log file
TESTPMD.LOG.
To use PMD you need quite some free memory. For large
programs this can be as much as 100K or even more. Under real
mode, the Post Mortem Debugger uses EMS or XMS memory if
that's available.
Chapter 3, Post Mortem Debugger 15
The Post-Mortem-Debugger under MS-Windows
───────────────────────────────────────────────────────────────────────────────
Under MS-Windows the Post-Mortem debugger has some extra
capabilities including the ability to give a stack dump when a gpf
occurs.
─────────────────────────────────────────────────────────────────────────────
Extra features
When the PMD unit is included in a windows program, you get
under Windows
the following extra features: MS-Windows:
■ Any text send to OutputDebugString is written to the log file
also.
■ Any error Windows displays, is written to the log file also.
■ If you make a parameter error when calling a Windows
function, this is logged. Almost no need for WinScope or
BoundsChecker anymore!
─────────────────────────────────────────────────────────────────────────────
GPF's under
GPF's are not caught under MS-Windows unlike under DPMI. If
Windows
your program crashes, Windows will still display the UAE dialog
box. If you want a symbolic stack dump in this case, you need to
call InstallIntHandler in the PMD unit.
InstallIntHandler is not called by default, because I couldn't get it
work right when using the debugger. Without the debugger,
everything runs fine. When I use the debugger to examine a
program, leave the debugger to go back to the IDE, edit something
and recompile, I'm bombed back to Dos, or Windows just hangs. I
would be indebted, if anyone has a fix for this.
16 Borland Pascal Debug Kit Manual
C H A P T E R
───────────────────────────────────────────────────────────────────────────────
4
TDInfo
The TDInfo unit is used by MemCheck and PMD to provide access
to the debug info stored in your executable. Information about this
debug info can be found in [Borland92]. You probably need this
book if you want to use TDInfo yourself.
Because this books contains quite a few errors, I refer the reader to
appendix A where I've listed OA.TXT, a file containing bugs and
omissions to chapter 4 of Borland's "Open Architecture Handbook
for Pascal".
How TDInfo models Borland's Debug Information
───────────────────────────────────────────────────────────────────────────────
Since the presented information in [Borland92] is less than clear,
I've used semantic principles (see [Bekke92]) to make the
information contained in the debug symbol tables and their
mutual relationships clearer. I'll explain the semantic approach
using the Turbo Debug Semantic Model presented in figure 4.1.
Semantic data modeling has two notions: types and relationships.
Types are basic data elements. Types are not stand-alone, but are
related to each other. As can be seen in figure 4.1, types are drawn
as rectangles. Relationships between types are drawn as lines.
A type can be related to another type in two ways:
■ When a type consists of other types we talk about an aggregated
type.
■ When a type is a special kind of another type, we talk about a
specialized type.
Aggregation can be seen in the Browser type, the uppermost
rectangle. Somewhat simplified, aggregation can be said to be
equal to the Pascal record type. A record type has a name and
consists of various fields. So does the Browser. Each Browser
1 to a SourceFile
record in the symbol table consists of a pointer
1The Symbol table actually does not store pointers, but record numbers
Chapter 4, TDInfo 17
Figure 4.1
Turbo Debug Semantic Browser
Model
LineNumber Correlation Symbol
oe─────
Type
Scope
───────
SourceFile Segment
Module
Name
18 Borland Pascal Debug Kit Manual
record, a LineNumber record and a Symbol record. The latter
record is also an aggregated type. It consists of a pointer to a
Module record, a Name record, a Scope record and a Type record.
There is no example of specialization in this figure, but
specialization can be compared to inheritance in Turbo Pascal.
The way the types (records) are drawn is also important. Read
from top to bottom, we always find fixed properties, i.e. a Browser
always contains a SourceFile. Read from bottom to top, we have
optional properties. Take for example the type Module. A Module
can have zero or more Segments. In the same way, a Scope can
have zero or more Symbols.
The relationships expressed in figure 4.1 show the ways you can
access information. If you have a certain line number, you first
have to search the correct LineNumber record. If you now want to
know in which source file this line appears, then you must first go
to the LineNumber's Correlation record and from there to the
SourceFile. If you want to know the name of the SourceFile you've
found, you can go directly from the SourceFile record to the Name
record.
In the semantic query language Xplain, we would express this as:
get LineNumber its Correlation its SourceFile its Name.
Note that you can also get the name of the source file in which the
line number appears in by going through Correlation its Segment
its Module and, finally, its Name.
Changes between Open Architecture names and TD-
Info names
───────────────────────────────────────────────────────────────────────────────
The TDInfo unit tries to model the debug symbol information as
semanticly correct as possible. Each type in TDInfo is prefixed with
a `T'. Note that I have changed some names so that they are not
consistent with [Borland92] (which is fairly liberal with naming
conventions to say the least).
A list of Open Architecture names and TDInfo names is presented
below. The TDInfo names refer to objects. The names in the Open
Architecture book refer to records. The types are listed in the order
they appear in the Open Architecture handbook.
Note that almost all fields in TDInfo's object are different from their
corresponding names in the Open Architecture Handbook too.
Chapter 4, TDInfo 19
Table 4.1
TDInfo name OA name
List of corresponding TDInfo
names and OA names TSymbol TSymbolRecord
TModule TModuleHeader
TSourceFile SourceFile
TLineNumber Line number
TScope Scope
TSegment Segment info
TCorrelation Typedef
TType Type Rec
TMember Struct offset rec
TClass TParent Table (?)
TParent TParent Table
TOverload Overload
TScopeClass TClassTable
TModuleClass LocalClass
TBrowser TDefinitionRecord
How to make use of TDInfo
───────────────────────────────────────────────────────────────────────────────
In this section some information is given how you can make use of
the TDInfo unit yourself. The information is this section is not yet
complete. You should really take a look at the sources of the PMD
unit and the AnnotateLog program (see ANNLOG.PAS, only in
the registered version of this program).
─────────────────────────────────────────────────────────────────────────────
Initializing the
Before you can make use of TDInfo, you need to initialize it by
TDInfo unit
calling the function TDInfoPresent. Pass TDInfoPresent a filename
which contains debug information. This file may be a stand-alone
symbol file created with TDStrip or an executable with debug
information appended to it. TDInfoPresent returns TRUE when it
has found debug information in the file. Only when it returns
TRUE is it save to use the other objects of the TDInfo unit.
─────────────────────────────────────────────────────────────────────────────
Getting information from a debug symbol
You can almost always directly express a `semantic query' using
TDInfo. To get the name of a module, you use TModule.ItsName.
To get the scope of a symbol you say TSymbol.ItsScope. These
ItsXXXX methods always return a pointer to the object that
file
corresponds with the name of the method.
When you call an ItsXXXX method for the first time, a new object
of that type is created. When you call the ItsXXXX method for the
second time, this object is returned again, so an object is not
created twice. You don't have to dispose these pointers, they are
disposed by the provider when you dispose the provider, i.e. if
20 Borland Pascal Debug Kit Manual
you have called TSymbol.ItsScope, TSymbol.Done disposes the
allocated TScope object.
Let's take a look at a basic example. Given an physical address,
Getting line
how can we display its the source file and line number? The first
numbers
thing we need to do is to turn physical addresses into logical
addresses. This is done by calling GetLogicalAddr. A physical
address goes in, a logical address comes out. Using this logical
address we can now proceed to find the source file and line
number. One approach would be to look the logical address up in
a .MAP file. Much easier is it to use the debug information
appended to an executable.
We get the LineNumber by calling the constructor
TLineNumber.AtAddr. For example:
LineNumber := New(PLineNumber, AtAddr(Addr));
LineNumber will be nil if no line number could be found. Else it
will contain a pointer to a TLineNumber object. To display the line
number we can write LineNumber^.Value. To get the name of the
source file, see also figure 4.1, we say
LineNumber^.ItsCorrelation^.ItsSourceFile^.ItsName.
Getting procedures
To get the procedure to which this line number belongs, we have
to know that a procedure is a symbol. To find a symbol at a given
address, we can use TSymbol.AtSegment. We need to pass it a
PSegment and an address. Because we already have a line number
(see the previous section), the segment is
LineNumber^.ItsCorrelation^.ItsSegment. The address is simply
the logical address. The AtSegment constructor does quite an
amount of work. It searches all scope records belonging to a
certain segment to find a scope which covers the given address. It
does this by calling TSegment.FirstScopeThat. This repeatedly
calls the passed function until this function returns true. If this
function returns TRUE, TSegment.FirstScopeThat returns the
scope record for which the function returned TRUE. Although this
is not displayed in figure 4.1, there exists a certain specialization of
Scope, which contains a pointer to a Symbol record.
TSymbol.AtSegment checks if TSegment.FirstScopeThat has
returned such a scope. If that's the case, TSymbol.AtSegment
loads the correct symbol record from the debug symbol table and
returns.
With the Symbol we now have, we have the procedure or function
to which this line number belongs. The name of the procedure or
Chapter 4, TDInfo 21
function is Symbol^.ItsName. To determine of we have a
procedure or a function we look at the type of the symbol. If
Symbol^.ItsType^.ReturnType = 1 we have a procedure , else we
have a function.
There is a lot more to say about TDInfo, but this should get you
started. In the PMD.PAS, ANNLOG.PAS and MEMCHECK.PAS
you find some other or more elaborated uses of TDInfo.
22 Borland Pascal Debug Kit Manual
C H A P T E R
───────────────────────────────────────────────────────────────────────────────
5
Assertions
Once you have acquainted yourself with Assertions, you will not
want to be without them! The Assertions unit provided here,
makes it easy for Borland Pascal programmers to use them. Add
Assertions to the USES clause of any unit or program which wants
to use assertions. In your code, simply call the procedure Assert
with a Boolean value and a string containing the message to be
printed if the Boolean value evaluates to FALSE. After the message
has been printed (which means that the assertion failed), your
program halts.
Example program:
program Example
uses
BBError,
Assertions;
begin
InitBBError('TEST.LOG', TRUE);
Assert(TRUE, 'This assertion passes.');
Assert(FALSE, 'This assertion fails.');
end.
If you call InitBBError , the message is written to the log file as well.
If you use the PMD unit (properly initialized!), Assert also prints a
symbolic stack trace.
You will probably want to use Assertions only in your test code,
not in your production code. All Assertions take time and space,
and some Assertions take quite a lot of time. That's no problem, or
less of a problem, as long as you're working with test code, but
your production code should be as fast as possible. Also, you
shouldn't need assertions there.
To conditionally include Assertions in your program, embrace
calls to Assert within a pair of conditional defines. The example
program then looks like this:
Chapter 5, Assertions 23
{$DEFINE Debug}
program Example
uses
BBError
{$IFDEF Debug}
, Assertions
{$ENDIF}
;
begin
InitBBError('TEST.LOG', TRUE);
{$IFDEF Debug}
Assert(TRUE, 'This assertion passes.');
Assert(FALSE, 'This assertion fails.');
{$ENDIF}
end.
If you enable the Debug directive, the Assertions are included. If
you disable the Debug directive, they are not included.
24 Borland Pascal Debug Kit Manual
P A R T
───────────────────────────────────────────────────────────────────────────────
2
Reference Manual
25
***************************************
E N D O F T H I S M A N U A L
***************************************
The complete reference manual is distributed with the registered
version only. Again, the reference manual looks exactly like to
Borland reference manuals.
The registered version manual also contains a list of bugs in the
Borland Pascal Open Architecture Book, compiled by Andy McFarland.